//
// C++ Implementation: Filesystem
//
// Description: 
//
//
// Author: James Molloy <james@jamesmolloy.co.uk>, (C) 2007
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include <Filesystem.h>
#include <Kernel.h>
#include <Directory.h>
#include <errno.h>
#include <ProcessManager.h>
#include <CharDevice.h>
#include <BlockDevice.h>
#include <RegularFile.h>

PathList Filesystem::expandPath(char *s)
{
  // First, check if the path is relative.
  if (s[0] != '/')
  {
    // We are relative.
    DEBUG_MSG("The path was " << s);
    PANIC("relative addresses not handled.");
    return PathList();
  }
  
  // Absolute path.
  // Go through the string, tokenising on '/'.
  char tmp[256];
  PathList pl;
  tmp[0] = '\0';
  int i = 0, j = 0;
  while (s[i] != '\0')
  {
    // Is this character a '/'?
    if (s[i] == '/')
    {
      // Was there any data in the token?
      if (j)
      {
        // Add the string to the path list.
        tmp[j] = '\0';
        char *tmp2 = new char[j+1];
        Kernel::strcpy(tmp2, tmp);
        pl.insertLast(tmp2);
        j = 0;
      }
    }
    else
    {
      tmp[j++] = s[i];
    }
    i++;
  }
  // Was there a token at the end of the string?
  if (j)
  {
    tmp[j] = '\0';
    char *tmp2 = new char[j+1];
    Kernel::strcpy(tmp2, tmp);
    pl.insertLast(tmp2);
  }
  
  return pl;
}

File *Filesystem::traverse(char *path)
{
  DEBUG_SPAM("Filesystem::traverse(\"" << path << "\").");
  PathList pl = expandPath(path);
  
  // start at root...
  Directory *curDir = root;
  File *toRet = (File*)root;
  while (pl.getLength())
  {
    // What's the file we're looking for?
    char *search = pl.removeFirst();
    // Does it exist?
    File *file = curDir->getEntry(search);
    // Make sure the character pointer is destroyed.
    delete [] search;
    
    if (!file)
    {
      // File not found. Release the rest of the pathlist and return NULL.
      while(pl.getLength())
      {
        delete [] pl.removeFirst();
      }
      toRet = NULL;
      break;
    }
    else
    {
      // We found the file! was it a directory?
      if (file->identify() == eFileType_Directory)
      {
        toRet = file;
        curDir = (Directory*)file;
        // Recurse.
        continue;
      }
      else
      {
        // It wasn't a directory. This is OK, as long as there are no 
        // more parts to path.
        if (pl.getLength())
        {
          // No, it's no good, we've run out of directories.
          // Delete all other strings and return NULL.
          while (pl.getLength())
          {
            delete [] pl.removeFirst();
          }
          file = NULL;
          break;
        }
        else
        {
          // Success! We found our file.
          toRet = file;
          break;
        }
      }
    }
  }
  // If we encountered an error, send back a 'not found' error.
  if (!toRet)
  {
    ERROR(ENOENT);
  }
  return toRet;
}

u32int Filesystem::traverseResource(char *path)
{
  File *f = traverse(path);
  if (f)
    return f->resource->getId();
  else
    return 0;
}

u32int Filesystem::mknod(FileTypes type, char *name, u32int resourceId, u32int impl)
{
  DEBUG_MSG("mknod(" << hex << type << ", \"" << name << "\", " << resourceId << ", " << impl << ").");
  char *newname = NULL;
  if (name)
  {
    newname = new char[Kernel::strlen(name)+1];
    Kernel::strcpy(newname, name);
    newname[Kernel::strlen(name)] = '\0';
  }
  
  File *tmp;
  switch(type)
  {
  case eFileType_Directory:
    tmp = new Directory(newname, this, resourceId);
    break;
  case eFileType_CharDevice:
    tmp = new CharDevice(newname, this, resourceId);
    break;
  case eFileType_BlockDevice:
    tmp = new BlockDevice(newname, this, resourceId);
    break;
  case eFileType_Symlink:
  case eFileType_RegularFile:
    tmp = new RegularFile(newname, this, resourceId);
    break;
  }
  tmp->setImplDefined(impl);
  if (name)
  {
    // We need to get the containing directory. So, if it contains a '/', the thing before it should be
    // passed to traverse(). if it doesn't contain a '/', just use root.
    u32int len = Kernel::strlen(name);
    int slash_loc = -1;
    for(int i = len; i >= 0; i--)
      if (name[i] == '/')
      {
        slash_loc = i;
        break;
      }

    Directory *dir = root;
    if (slash_loc > 0)
    {
      char *tmpdir = new char[slash_loc];
      Kernel::memcpy((u8int*)tmpdir, (u8int*)name, slash_loc);
      tmpdir[slash_loc] = '\0';
      DEBUG_MSG("populate: containing dir: " << tmpdir);
      dir = (Directory*)traverse(tmpdir);
      delete [] tmpdir;
    }
    if (!dir)
      return 0;
    dir->addEntry(tmp);
  }
  return tmp->resource->getId();
}
